package tenants

import (
	"context"
	"fmt"
	"github.com/Masterminds/squirrel"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"product/common/datetime"
	"product/common/validator"
)

var _ TenantsModel = (*customTenantsModel)(nil)

type (
	// TenantsModel is an interface to be customized, add more methods here,
	// and implement the added methods in customTenantsModel.
	TenantsModel interface {
		tenantsModel
		withSession(session sqlx.Session) TenantsModel
		// CheckTenantExists 校验租户是否存在
		CheckTenantExists(ctx context.Context, tenantName string) (bool, error)
		// WithTrans 使用事务
		WithTrans(ctx context.Context, fn func(context.Context, sqlx.Session) error) error
		// CheckTenantCodeExists 检查信用代码是否注册
		CheckTenantCodeExists(ctx context.Context, code string) (bool, error)
		// ModifyTenant 修改租户
		ModifyTenant(ctx context.Context, data *Tenants) error
		// SaveTenant 保存之后返回租户ID
		SaveTenant(ctx context.Context, data *Tenants) (int64, error)
		// DeleteTenant 删除租户
		DeleteTenant(ctx context.Context, id int64) error
		// FindTenantByID 根据ID查询租户
		FindTenantByID(ctx context.Context, id int64) (*Tenants, error)
		// FindTenantPage 分页查询租户
		FindTenantPage(ctx context.Context, page, pageSize int64, conditions map[string]interface{}) ([]*Tenants, error)
		// FindTenantByWebsite 根据网站查询租户
		FindTenantByWebsite(ctx context.Context, website string) (*Tenants, error)
		// FindTenantByName 通过租户名称查找租户信息
		FindTenantByName(ctx context.Context, name string) (*Tenants, error)
	}

	customTenantsModel struct {
		*defaultTenantsModel
	}
)

func (m *customTenantsModel) FindTenantByName(ctx context.Context, name string) (*Tenants, error) {
	sql := squirrel.Select(tenantsRows).From(m.table).Where("Name = ?", name).Limit(1)
	query, values, err := sql.ToSql()
	if err != nil {
		return nil, err
	}
	var tenant Tenants
	err = m.conn.QueryRowCtx(ctx, &tenant, query, values...)
	switch err {
	case nil:
		return &tenant, nil
	case sqlx.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *customTenantsModel) FindTenantByWebsite(ctx context.Context, website string) (*Tenants, error) {
	sql := squirrel.Select(tenantsRows).From(m.table).Where("TenantDomain = ?", website).Limit(1)
	query, values, err := sql.ToSql()
	if err != nil {
		return nil, err
	}
	var tenant Tenants
	err = m.conn.QueryRowCtx(ctx, &tenant, query, values...)
	switch err {
	case nil:
		return &tenant, nil
	case sqlx.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *customTenantsModel) SaveTenant(ctx context.Context, data *Tenants) (int64, error) {
	sql := squirrel.Select("ID").From(m.table).Where("Name = ?", data.Name).Limit(1)
	query, values, err := sql.ToSql()
	if err != nil {
		return 0, err
	}
	var id int64
	err = m.conn.QueryRowCtx(ctx, &id, query, values...)
	if err == nil {
		return id, nil
	}
	_, err = m.Insert(ctx, data)
	if err == nil {
		id = data.ID
	}
	return id, err
}

func (m *customTenantsModel) ModifyTenant(ctx context.Context, data *Tenants) error {
	sql := m.genModifyTenantSql(data).Where("ID = ?", data.ID)
	query, values, err := sql.ToSql()
	if err != nil {
		return err
	}
	_, err = m.conn.ExecCtx(ctx, query, values...)
	return err
}

func (m *customTenantsModel) DeleteTenant(ctx context.Context, id int64) error {
	query, values, err := m.genDeleteTenantSql(id).Set("ModifiedDate", datetime.GetNowTimestamp()).ToSql()
	if err != nil {
		return err
	}
	_, err = m.conn.ExecCtx(ctx, query, values...)
	return err
}

func (m *customTenantsModel) FindTenantByID(ctx context.Context, id int64) (*Tenants, error) {
	query, values, err := m.genFindTenantByIDSql(id).ToSql()
	if err != nil {
		return nil, err
	}
	var resp Tenants
	err = m.conn.QueryRowCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return &resp, nil
	case sqlx.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *customTenantsModel) FindTenantPage(ctx context.Context, page, pageSize int64,
	conditions map[string]interface{}) ([]*Tenants, error) {
	sql := m.genFindTenantPageSql(page, pageSize, conditions)
	query, values, err := sql.ToSql()
	if err != nil {
		return nil, err
	}
	var resp []*Tenants
	err = m.conn.QueryRowsCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	case sqlx.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *customTenantsModel) CheckTenantCodeExists(ctx context.Context, code string) (bool, error) {
	query, values, err := m.genCheckTenantCodeExistsSql(code).ToSql()
	if err != nil {
		return false, err
	}
	fmt.Println(query)
	var id int64
	err = m.conn.QueryRowCtx(ctx, &id, query, values...)
	switch err {
	case nil:
		return id > 0, nil
	case sqlx.ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

func (m *customTenantsModel) WithTrans(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
	return m.conn.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
		return fn(ctx, session)
	})
}

func (m *customTenantsModel) CheckTenantExists(ctx context.Context, tenantName string) (bool, error) {
	query := fmt.Sprintf("select count(ID) from %s where `Name` = ?", m.table)
	var count int64
	err := m.conn.QueryRowCtx(ctx, &count, query, tenantName)
	switch err {
	case nil:
		return count > 0, nil
	case sqlx.ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

// NewTenantsModel returns a model for the database table.
func NewTenantsModel(conn sqlx.SqlConn) TenantsModel {
	return &customTenantsModel{
		defaultTenantsModel: newTenantsModel(conn),
	}
}

func (m *customTenantsModel) withSession(session sqlx.Session) TenantsModel {
	return NewTenantsModel(sqlx.NewSqlConnFromSession(session))
}

// ########## 私有方法 #########
func (m *customTenantsModel) genDeleteTenantSql(id int64) squirrel.UpdateBuilder {
	sql := squirrel.Update(m.table).Where(squirrel.Eq{"ID": id}).Set("IsDeleted", 1)
	return sql
}

func (m *customTenantsModel) genModifyTenantSql(data *Tenants) squirrel.UpdateBuilder {
	sql := squirrel.Update(m.table)
	if data.Status != 0 {
		sql = sql.Set("Status", data.Status)
	}
	if validator.IsEmptyString(data.Contact) {
		sql = sql.Set("Contact", data.Contact)
	}
	if validator.IsEmptyString(data.ContactPhone) {
		sql = sql.Set("ContactPhone", data.ContactPhone)
	}
	if data.Email.Valid {
		sql = sql.Set("Email", data.Email)
	}
	if data.Telephone.Valid {
		sql = sql.Set("Telephone", data.Telephone)
	}
	if !validator.IsEmptyString(data.RegisteredAddress) {
		sql = sql.Set("RegisteredAddress", data.RegisteredAddress)
	}
	if !validator.IsEmptyString(data.Legal) {
		sql = sql.Set("Legal", data.Legal)
	}
	if data.RegisteredCapital.Valid {
		sql = sql.Set("RegisteredCapital", data.RegisteredCapital)
	}
	if data.DateOfActivation.Valid {
		sql = sql.Set("DateOfActivation", data.DateOfActivation)
	}
	if data.EstablishmentDate.Valid {
		sql = sql.Set("EstablishmentDate", data.EstablishmentDate)
	}
	if data.ExpirationDate > 0 {
		sql = sql.Set("ExpirationDate", data.ExpirationDate)
	}
	if data.RegistrationDate != 0 {
		sql = sql.Set("RegistrationDate", data.RegistrationDate)
	}
	if data.Validity.Valid {
		sql = sql.Set("Validity", data.Validity)
	}
	if data.TaxID.Valid {
		sql = sql.Set("TaxID", data.TaxID)
	}
	if data.Region.Valid {
		sql = sql.Set("Region", data.Region)
	}
	if data.NickName.Valid {
		sql = sql.Set("NickName", data.NickName)
	}
	return sql.Set("ModifiedDate", datetime.GetNowTimestamp())
}

func (m *customTenantsModel) genFindTenantByIDSql(id int64) squirrel.SelectBuilder {
	sql := squirrel.Select(tenantsRows).Where("ID = ?", id).Where("IsDeleted = 0").From(m.table)
	//fmt.Println(sql)
	return sql
}
func (m *customTenantsModel) genFindTenantPageSql(page, pageSize int64,
	conditions map[string]interface{}) squirrel.SelectBuilder {
	sql := squirrel.Select(tenantsRows)
	return sql.Where("IsDeleted = 0").Where(conditions).Offset(uint64(page - 1)).Limit(uint64(pageSize))
}

func (m *customTenantsModel) genCheckTenantCodeExistsSql(code string) squirrel.SelectBuilder {
	return squirrel.Select("ID").From(m.table).Where("Code = ?", code).Where("IsDeleted = 0").Limit(1)
}
